﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Diagnostics.Contracts;
using RaisingForce.Raise.Language.Rules;

namespace RaisingForce.Raise.Language.Model
{
    sealed public class WordFabric :
        IWordFabric
    {
        #region Constructors

        public WordFabric(IWordTypePool wordTypePool, IGlyphPool glyphPool)
        {
            if (wordTypePool == null)
            {
                throw new ArgumentNullException("wordTypePool");
            }
            if (glyphPool == null)
            {
                throw new ArgumentNullException("glyphPool");
            }
            Contract.EndContractBlock();
            this.wordTypePool = wordTypePool;
            this.glyphPool = glyphPool;
        }

        public WordFabric()
        {
            this.wordTypePool =
                new WordTypePool(registerDefaultWordTypes: true);
            this.glyphPool =
                new GlyphPool(registerDefaultContainers: true);
        }

        #endregion

        #region Private Declarations

        private readonly IWordTypePool wordTypePool;

        private readonly IGlyphPool glyphPool;

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(this.wordTypePool != null);
            Contract.Invariant(this.glyphPool != null);
        }

        private Glyph getGlyph(Type glyphType, string glyphValue)
        {
            Contract.Requires(glyphType != null);
            Contract.Requires(glyphValue == null || GlyphRules.ValueIsValid(glyphValue));
            Contract.Ensures(Contract.Result<Glyph>() != null);
            return glyphValue != null ?
                this.glyphPool.GetGlyph(glyphType, glyphValue) :
                this.glyphPool.GetSingletonGlyph(glyphType);
        }

        #endregion

        #region IWordFabric Methods

        public T CreateWord<T>(
            string wordTypeName,
            string primaryGlyphValue = null,
            string secondaryGlyphValue = null)
            where T : Word
        {
            if (!Word.TypeIsAllowed(typeof(T)))
            {
                throw new ArgumentException("T");
            }
            if (!WordTypeRules.NameIsValid(wordTypeName))
            {
                throw new ArgumentException("wordTypeName");
            }
            if (primaryGlyphValue != null
                && !GlyphRules.ValueIsValid(primaryGlyphValue))
            {
                throw new ArgumentException("primaryGlyphValue");
            }
            if (secondaryGlyphValue != null
                && !GlyphRules.ValueIsValid(secondaryGlyphValue))
            {
                throw new ArgumentException("secondaryGlyphValue");
            }
            WordType wordType = this.wordTypePool.GetWordType(wordTypeName);
            Glyph primaryGlyph = this.getGlyph(wordType.PrimaryGlyphType, primaryGlyphValue);
            Contract.Assume(primaryGlyph.GetType() == wordType.PrimaryGlyphType);
            Type secondaryGlyphType = wordType.SecondaryGlyphType;
            Glyph secondaryGlyph =
                secondaryGlyphType != null ?
                this.getGlyph(secondaryGlyphType, secondaryGlyphValue) :
                null;
            Contract.Assume(secondaryGlyph == null || secondaryGlyph.GetType() == wordType.SecondaryGlyphType);
            return Word.Create<T>(wordType, primaryGlyph, secondaryGlyph);
        }

        #endregion
    }
}
